package samples

import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withContext
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.system.measureTimeMillis

/**
 *  线程（协程）安全（同步、原子性）
 * @author mph
 * @date 2025/1/17
 */
@OptIn(ExperimentalCoroutinesApi::class)
class ThreadSafeTest {

    /**
     * volatile并不会保证原子性
     */
    @Volatile // in Kotlin `volatile` is an annotation
    var counter = 0

    @Test
    fun test1() = runTest {
        withContext(Dispatchers.Default) {
            massiveRun {
                counter++
            }
        }
        println("Counter = $counter")
    }

    /**
     * AtomicXxx API
     */
    val counter2 = AtomicInteger()

    @Test
    fun test2() = runTest {
        val time = measureTimeMillis {
            withContext(Dispatchers.Default) {
                massiveRun {
                    counter2.incrementAndGet()
//                log(counter2)
                }
            }
        }
        println("Counter = $counter2 || Time : $time")
    }

    /**
     * 使用限制在单个线程上执行的方法可以有效避免同步逻辑，同时单个线程上的多协程又能保持并发执行的效率
     */
    var counter3 = 0

    @OptIn(DelicateCoroutinesApi::class)
    @Test
    fun test3() = runTest {
        val singleThread = newSingleThreadContext("Counter context")
        val time = measureTimeMillis {
            /*//fine-grained thread confinement细颗粒度的线程约束
            //太慢，因为每次 counter3++ 都会切换到 singleThread 上执行，所以性能很差
            withContext(Dispatchers.Default) {
                massiveRun {
                    withContext(singleThread) {
                        counter3++
                    }
                }
            }*/
            //coarse-grained thread confinement粗颗粒度的线程约束
            withContext(singleThread) {
                massiveRun {
                    counter3++
//                    log(counter3)
                }
            }
        }
        println("Counter = $counter2 || Time : $time")
    }

    /**
     * 互斥、锁
     */
    val mutex = Mutex()

    @Test
    fun test4() = runTest {
        withContext(Dispatchers.Default) {
            // protect each increment with lock
            mutex.withLock {
                massiveRun {
                    counter3++
                }
            }
           /* //等价于
            try {
                mutex.lock()
                massiveRun {
                    counter3++
                }
            } finally {
                mutex.unlock()
            }*/
        }
        println("Counter = $counter")
    }

    suspend fun massiveRun(action: suspend () -> Unit) {
        val n = 100  // number of coroutines to launch
        val k = 1000 // times an action is repeated by each coroutine
        val time = measureTimeMillis {
            coroutineScope { // scope for coroutines
                repeat(n) {
                    launch {
                        repeat(k) { action() }
                    }
                }
            }
        }
        println("Completed ${n * k} actions in $time ms")
    }

}